from json import JSONDecodeError
import hashlib
import urllib.parse
from time import time

from .auth import Auth
from .error import BiliAPIError
from .util import *

__requests__ = True
try:
    import requests
except ImportError:
    __requests__ = False


class Biliapi:
    """
    主类
    """

    def __init__(self, ak, sk):
        self.ak = ak
        self.sk = sk
        self._return_url = not __requests__
        self._s = requests.Session()
        self._a_k = None

    def get_return_url(self):
        """
        是否返回URL
        :return:
        """
        return self._return_url

    def set_return_url(self, return_url):
        """
        设置是否请求
        :param return_url: 是否返回URL
        """
        if return_url and not __requests__:
            raise BiliAPIError("You have no lib: requests!")
        self._return_url = return_url

    def _url_builder(self, url, params, android=False, sign=True):
        """generate sign & build request url"""
        if sign:
            params['appkey'] = self.ak
        if android:  # 安卓API需要加入特定参数
            params['actionKey'] = 'appkey'
            params['build'] = '414000'
            params['platform'] = 'android'
            params['mobi_app'] = 'android'
            params['ts'] = int(time()) * 1000
        if self._a_k is not None:
            if 'access_key' not in params:  # 加入access_key
                params['access_key'] = self._a_k
        params = sort_dict_by_key(params)  # 为sign计算准备
        param = ''
        for p in params:
            if p[0] != '':
                param = param + p[0] + '=' + str(urllib.parse.quote(str(p[1]))) + '&'
        param = param[:-1]
        if not sign:
            return append_param(url, param)
        # 计算sign
        md5 = hashlib.md5((param + self.sk).encode("utf-8"))
        return append_param(url, param + '&sign=' + md5.hexdigest())

    def request(self, url, params=None, method='GET', body=None, android=False, sign=True):
        """
        请求并返回数据，默认json就解析。如果设置了则返回拼接的url，格式参见最上。
        :param url: API地址
        :param params: 调用参数
        :param method: 调用方法，默认GET
        :param body: 正文，仅POST方法使用
        :param android: 是否为android的API，默认True
        :param sign: 是否需要计算sign，默认True
        :return: 返回值，或者为url
        """
        if body is None:
            body = {}
        if params is None:
            params = {}
        u = self._url_builder(url=url, params=params, android=android, sign=sign)  # 反正不管怎么样都是要拼接的
        url_dict = {
            'method': method,
            'url': u
        }
        if method == 'GET':
            if self._return_url:
                return url_dict
            # 开始请求
            r = self._s.get(url_dict['url'])
            try:
                return r.json()
            except JSONDecodeError:
                return r.text
        elif method == 'POST':
            url_dict['body'] = body
            if self._return_url:
                return url_dict
            # 开始请求
            r = self._s.post(url_dict['url'], data=body)
            try:
                return r.json()
            except JSONDecodeError:
                return r.text
        else:  # 啊呜什么鬼啦
            raise BiliAPIError("Unknown request method!")

    def get_session(self):
        """
        获得请求用session
        :return:
        """
        return self._s

    def get_auth(self):
        """
        获得鉴权对象
        :return:
        """
        return Auth(self, self._a_k, self._s.cookies)

    def auth(self, auth):
        """
        鉴权
        :param auth:
        :return:
        """
        if not isinstance(auth, Auth):
            raise BiliAPIError('Illegal type of param auth.')
        self._a_k = auth.access_key
        if __requests__:
            self._s.cookies = auth.cookie

    def check_auth(self):
        """
        检查是否登录成功
        :return:
        """
        return len(self._a_k) > 1  # 简单粗暴
